package br.uece.laconi.tac.ga;

import jmetal.base.DecisionVariables;
import jmetal.base.Problem;
import jmetal.base.Solution;
import jmetal.base.Configuration.SolutionType_;
import jmetal.base.Configuration.VariableType_;
import jmetal.base.variable.Binary;
import jmetal.util.JMException;

public class Teste extends Problem {

	public Teste(String solutionType) {
		numberOfVariables_ = 4;
		numberOfObjectives_ = 2;
		numberOfConstraints_ = 1;
		problemName_ = "Teste";

		upperLimit_ = new double[numberOfVariables_];
		lowerLimit_ = new double[numberOfVariables_];

		// for (int i = 0; i < numberOfVariables_; i++) {
		// lowerLimit_[i] = 0;
		// upperLimit_[i] = 3;
		// }
		lowerLimit_[0] = 0;
		upperLimit_[0] = 5;
		lowerLimit_[1] = 0;
		upperLimit_[1] = 2;
		lowerLimit_[2] = 0;
		upperLimit_[2] = 3;
		lowerLimit_[3] = 0;
		upperLimit_[3] = 4;

		solutionType_ = Enum.valueOf(SolutionType_.class, solutionType);

		// All the variables are of the same type, so the solutionType name is
		// the
		// same than the variableType name
		variableType_ = new VariableType_[numberOfVariables_];
		for (int var = 0; var < numberOfVariables_; var++) {
			variableType_[var] = Enum
					.valueOf(VariableType_.class, solutionType);
		} // for
	}

	@Override
	public void evaluate(Solution solution) throws JMException {
		DecisionVariables decisionVariables = solution.getDecisionVariables();
		solution.setObjective(0, calculateFitness(decisionVariables));
		solution.setObjective(1, getTotalNumberOfCoins(decisionVariables));
	}

	// int [] pref = new int [] {150, 100, 20};
	// public double utilidade(DecisionVariables decisionVariables) throws
	// JMException{
	// double aux = 0.0;
	// for (int i = 0; i < decisionVariables.variables_.length; i++) {
	// double aux2 = decisionVariables.variables_[i].getValue();
	// aux = (aux < aux2 ? aux2 : aux);
	// }
	// return 0d;
	// }

	public double calculateFitness(DecisionVariables decisionVariables)
			throws JMException {
		// The fitness value measures both how close the value is to the
		// target amount supplied by the user and the total number of coins
		// represented by the solution. We do this in two steps: first,
		// we consider only the represented amount of change vs. the target
		// amount of change and calculate higher fitness values for amounts
		// closer to the target, and lower fitness values for amounts further
		// away from the target. If the amount equals the target, then we go
		// to step 2, which adjusts the fitness to a higher value for
		// solutions representing fewer total coins, and lower fitness
		// values for solutions representing a larger total number of coins.
		// ------------------------------------------------------------------
		int changeAmount = amountOfChange(decisionVariables);
		// int totalCoins = getTotalNumberOfCoins( decisionVariables );
		// int changeDifference = Math.abs( 99 - changeAmount );

		// Step 1: Determine the distance of the amount represented by the
		// solution from the target amount. Since we know the maximum amount
		// of change is 99 cents, we'll subtract from that the difference
		// between the solution amount and the target amount. That will give
		// the desired effect of returning higher values for amounts close
		// to the target amount and lower values for amounts further away
		// from the target amount.
		// ------------------------------------------------------------------
		// double fitness = (double)( 99 - changeDifference );

		// Step 2: If the solution amount equals the target amount, then
		// we add additional fitness points for solutions representing fewer
		// total coins.
		// -----------------------------------------------------------------
		// if( changeAmount == 99)
		// {
		// fitness += 100 - ( 10 * totalCoins );
		// }

		// return fitness;
		return -changeAmount;// - totalCoins);
	}

	/**
	 * Calculates the total amount of change (in cents) represented by the given
	 * chromosome and returns that amount.
	 * 
	 * @param a_potentialSolution
	 *            The potential solution to evaluate.
	 * @return The total amount of change (in cents) represented by the given
	 *         solution.
	 */
	public int amountOfChange(DecisionVariables decisionVariables)
			throws JMException {
		int numQuarters = (int) decisionVariables.variables_[0].getValue();
		int numDimes = (int) decisionVariables.variables_[1].getValue();
		int numNickels = (int) decisionVariables.variables_[2].getValue();
		int numPennies = (int) decisionVariables.variables_[3].getValue();

		return (numQuarters * 25) + (numDimes * 10) + (numNickels * 5)
				+ numPennies;
	}

	/**
	 * Retrieves the number of coins represented by the given potential solution
	 * at the given gene position.
	 * 
	 * @param a_potentialSolution
	 *            The potential solution to evaluate.
	 * @param a_position
	 *            The gene position to evaluate.
	 * @return the number of coins represented by the potential solution at the
	 *         given gene position.
	 */
	public double getNumberOfCoinsAtGene(DecisionVariables decisionVariables,
			int a_position) throws JMException {
		int numCoins = (int) decisionVariables.variables_[a_position]
				.getValue();

		return numCoins;
	}

	/**
	 * Returns the total number of coins represented by all of the genes in the
	 * given chromosome.
	 * 
	 * @param a_potentialsolution
	 *            The potential solution to evaluate.
	 * @return The total number of coins represented by the given Chromosome.
	 */
	public int getTotalNumberOfCoins(DecisionVariables decisionVariables)
			throws JMException {
		int totalCoins = 0;

		for (int i = 0; i < numberOfVariables_; i++) {
			totalCoins += getNumberOfCoinsAtGene(decisionVariables, i);
		}

		return totalCoins;
	}

	@Override
	public void evaluateConstraints(Solution solution) throws JMException {
		DecisionVariables decisionVariables = solution.getDecisionVariables();

		solution.setNumberOfViolatedConstraint(0);
		solution.setOverallConstraintViolation(0);

        int numQuarters = (int)decisionVariables.variables_[0].getValue() ;
        int numDimes = (int)decisionVariables.variables_[1].getValue() ;
        int numNickels = (int)decisionVariables.variables_[2].getValue() ;
        int numPennies = (int)decisionVariables.variables_[3].getValue() ;

        if ((numQuarters + numDimes + numNickels + numPennies) < 1) {
			solution.setNumberOfViolatedConstraint(1);
			solution.setOverallConstraintViolation(-1000);
		}

		if (((numQuarters * 25) + (numDimes * 10) + (numNickels * 5) + numPennies) > 99) {
			solution.setNumberOfViolatedConstraint(1);
			solution.setOverallConstraintViolation(99 - ((numQuarters * 25)
					+ (numDimes * 10) + (numNickels * 5) + numPennies));
		}
	}
}
